/*
 *    Copyright (c) 2018-2025, boss All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the boss developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: boss
 */
package com.hopes.bizz.risk.control.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hopes.bizz.common.core.util.CollectionUtils;
import com.hopes.bizz.common.core.util.DateUtils;
import com.hopes.bizz.common.core.util.StringUtils;
import com.hopes.bizz.risk.common.constant.enums.RiskModDictEnum;
import com.hopes.bizz.risk.common.constant.enums.RiskSysDictEnum;
import com.hopes.bizz.risk.control.dto.RiskUnitExcelDTO;
import com.hopes.bizz.risk.control.entity.RiskMeasureTaskEntity;
import com.hopes.bizz.risk.control.entity.RiskUnitEntity;
import com.hopes.bizz.risk.control.entity.RiskUnitEventEntity;
import com.hopes.bizz.risk.control.mapper.RiskMeasureTaskMapper;
import com.hopes.bizz.risk.control.mapper.RiskUnitEventMapper;
import com.hopes.bizz.risk.control.mapper.RiskUnitMapper;
import com.hopes.bizz.risk.control.service.RiskUnitEventService;
import com.hopes.bizz.risk.control.service.RiskUnitService;
import com.hopes.bizz.risk.control.vo.RiskUnitExcelVO;
import com.hopes.bizz.risk.control.vo.RiskUnitVO;
import com.hopes.boss.admin.api.resolver.DictResolver;
import com.hopes.boss.admin.api.resolver.enums.AdminDictEnum;
import com.hopes.boss.common.core.constant.CommonConstants;
import com.hopes.boss.common.core.util.R;
import com.hopes.boss.common.entity.ValidateImportBO;
import com.hopes.boss.common.excel.vo.ErrorMessage;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 风险分析单元
 *
 * @author daijie
 * @date 2023-07-17 18:53:13
 */
@Service
public class RiskUnitServiceImpl extends ServiceImpl<RiskUnitMapper, RiskUnitEntity> implements RiskUnitService {

    @Resource
    private RiskUnitEventMapper riskUnitEventMapper;
    @Resource
    private RiskMeasureTaskMapper riskMeasureTaskMapper;
    @Lazy
    @Resource
    private RiskUnitEventService riskUnitEventService;

    @Override
    public ValidateImportBO<RiskUnitExcelDTO> validateImport(List<RiskUnitExcelDTO> excelDTOList) {
        ValidateImportBO<RiskUnitExcelDTO> bo = new ValidateImportBO<>();
        List<ErrorMessage> errorMessageList = new ArrayList<>();
        List<RiskUnitExcelDTO> saveList = new ArrayList<>();
        int successNum = 0;
        int failedNum = 0;

        List<RiskUnitEntity> list = this.list();
        for (RiskUnitExcelDTO excelDTO : excelDTOList) {
            // 检验风险单元名称是否存在
            if (list.stream().anyMatch(item -> item.getRiskName().equals(excelDTO.getRiskName()))) {
                errorMessageList.add(new ErrorMessage(excelDTO.getLineNum(), new HashSet<String>() {{
                    add("风险单元名称不允许重复!" + "  " + excelDTO.getRiskName());
                }}));
                failedNum++;
                continue;
            } else if (StringUtils.isBlank(excelDTO.getRiskObjectId())) {
                errorMessageList.add(new ErrorMessage(excelDTO.getLineNum(), new HashSet<String>() {{
                    add("所属风险对象不允许为空!" + "  " + excelDTO.getRiskName());
                }}));
                failedNum++;
                continue;
            } else if (StringUtils.isBlank(excelDTO.getRiskUnitType())) {
                errorMessageList.add(new ErrorMessage(excelDTO.getLineNum(), new HashSet<String>() {{
                    add("风险单元类型不允许为空!" + "  " + excelDTO.getRiskName());
                }}));
                failedNum++;
                continue;
            } else if (StringUtils.isBlank(excelDTO.getReportDataFlag())) {
                errorMessageList.add(new ErrorMessage(excelDTO.getLineNum(), new HashSet<String>() {{
                    add("数据是否上报不允许为空!" + "  " + excelDTO.getRiskName());
                }}));
                failedNum++;
                continue;
            } else if (StringUtils.isBlank(excelDTO.getDeptId())) {
                errorMessageList.add(new ErrorMessage(excelDTO.getLineNum(), new HashSet<String>() {{
                    add("责任部门不允许为空!" + "  " + excelDTO.getRiskName());
                }}));
                failedNum++;
                continue;
            } else if (StringUtils.isBlank(excelDTO.getPrincipal())) {
                errorMessageList.add(new ErrorMessage(excelDTO.getLineNum(), new HashSet<String>() {{
                    add("责任人不允许为空!" + "  " + excelDTO.getRiskName());
                }}));
                failedNum++;
                continue;
            }

            saveList.add(excelDTO);
            successNum++;
        }

        bo.setErrorMessages(errorMessageList);
        bo.setSaveList(saveList);
        bo.setSuccessNum(successNum);
        bo.setFailedNum(failedNum);
        return bo;
    }

    @Override
    public RiskUnitEntity importDtoToEntity(RiskUnitExcelDTO excelDto) {
        RiskUnitEntity entity= new RiskUnitEntity();
        BeanUtils.copyProperties(excelDto, entity);

        if (StringUtils.isNotBlank(excelDto.getRiskObjectId())) {
            entity.setRiskObjectId(Long.valueOf(DictResolver.getItemValueByLabel(RiskModDictEnum.risk_object, excelDto.getRiskObjectId())));
        }
        if (StringUtils.isNotBlank(excelDto.getRiskUnitType())) {
            entity.setRiskUnitType(Integer.valueOf(DictResolver.getItemValueByLabel(RiskSysDictEnum.risk_unit_type, excelDto.getRiskUnitType())));
        }
        if (StringUtils.isNotBlank(excelDto.getReportDataFlag())) {
            entity.setReportDataFlag(StringUtils.getUseFlag(excelDto.getReportDataFlag()));
        }
        if (StringUtils.isNotBlank(excelDto.getControlLevel())) {
            entity.setControlLevel(DictResolver.getItemValueByLabel(RiskSysDictEnum.risk_control_level, excelDto.getControlLevel()));
        }
        if (StringUtils.isNotBlank(excelDto.getServiceDate())) {
            entity.setServiceDate(DateUtils.parseDate(excelDto.getServiceDate(), null));
        }
        if (StringUtils.isNotBlank(excelDto.getIdentifyFlag())) {
            entity.setIdentifyFlag(StringUtils.getUseFlag(excelDto.getIdentifyFlag()));
        }
        if (StringUtils.isNotBlank(excelDto.getRiskLevel())) {
            entity.setRiskLevel(Integer.valueOf(DictResolver.getItemValueByLabel(RiskSysDictEnum.risk_level, excelDto.getRiskLevel())));
        }
        if (StringUtils.isNotBlank(excelDto.getIdentifyMethod())) {
            entity.setIdentifyMethod(DictResolver.getItemValueByLabel(RiskSysDictEnum.risk_identify_method, excelDto.getIdentifyMethod()));
        }

        if (StringUtils.isNotBlank(excelDto.getEvaluateDate())) {
            entity.setEvaluateDate(DateUtils.parseDate(excelDto.getEvaluateDate(), null));
        }
        if (StringUtils.isNotBlank(excelDto.getReevaluateDate())) {
            entity.setReevaluateDate(DateUtils.parseDate(excelDto.getReevaluateDate(), null));
        }
        if (StringUtils.isNotBlank(excelDto.getAuditDate())) {
            entity.setAuditDate(DateUtils.parseDate(excelDto.getAuditDate(), null));
        }

        if(StringUtils.isNotBlank(excelDto.getDeptId())) {
            entity.setDeptId(Arrays.stream(excelDto.getDeptId().split(StringUtils.COMMA))
                    .map(it -> DictResolver.getItemValueByLabel(AdminDictEnum.sys_dept, it)).collect(Collectors.joining(",")));
        }
        if(StringUtils.isNotBlank(excelDto.getIdentifyUserId())) {
            entity.setIdentifyUserId(Long.valueOf(DictResolver.getItemValueByLabel(AdminDictEnum.sys_user, excelDto.getIdentifyUserId())));
        }
        if(StringUtils.isNotBlank(excelDto.getEvaluateUserId())) {
            entity.setEvaluateUserId(Long.valueOf(DictResolver.getItemValueByLabel(AdminDictEnum.sys_user, excelDto.getEvaluateUserId())));
        }
        if(StringUtils.isNotBlank(excelDto.getAuditUserId())) {
            entity.setAuditUserId(Long.valueOf(DictResolver.getItemValueByLabel(AdminDictEnum.sys_user, excelDto.getAuditUserId())));
        }
        if(StringUtils.isNotBlank(excelDto.getEvaluateMember())) {
            entity.setEvaluateMember(Arrays.stream(excelDto.getEvaluateMember().split(StringUtils.COMMA))
                    .map(it -> DictResolver.getItemValueByLabel(AdminDictEnum.sys_user, it)).collect(Collectors.joining(",")));
        }

        if(StringUtils.isNotBlank(excelDto.getPrincipal())) {
            entity.setPrincipal(Arrays.stream(excelDto.getPrincipal().split(StringUtils.COMMA))
                    .map(it -> DictResolver.getItemValueByLabel(AdminDictEnum.sys_user, it)).collect(Collectors.joining(",")));
        }
        return entity;
    }

    @Override
    public RiskUnitExcelVO exportEntityToVo(RiskUnitEntity entity) {
        RiskUnitExcelVO excelVO = new RiskUnitExcelVO();
        BeanUtils.copyProperties(entity, excelVO);

        excelVO.setRiskObjectId(DictResolver.getItemLabelByValue(RiskModDictEnum.risk_object, entity.getRiskObjectId()));
        excelVO.setRiskUnitType(DictResolver.getItemLabelByValue(RiskSysDictEnum.risk_unit_type, entity.getRiskUnitType()));
        if (ObjectUtil.isNotNull(entity.getReportDataFlag())) {
            excelVO.setReportDataFlag(StringUtils.getUseFlagStr(entity.getReportDataFlag()));
        }
        excelVO.setDeptId(DictResolver.getItemLabelByValue(AdminDictEnum.sys_dept, entity.getDeptId()));
        excelVO.setPrincipal(DictResolver.getItemLabelByValue(AdminDictEnum.sys_user, entity.getPrincipal()));
        excelVO.setControlLevel(DictResolver.getItemLabelByValue(RiskSysDictEnum.risk_control_level, entity.getControlLevel()));
        excelVO.setServiceDate(DateUtils.limitDay(entity.getServiceDate()));
        if (ObjectUtil.isNotNull(entity.getIdentifyFlag())) {
            excelVO.setIdentifyFlag(StringUtils.getUseFlagStr(entity.getIdentifyFlag()));
        }
        excelVO.setEvaluateMethod(DictResolver.getItemLabelByValue(RiskSysDictEnum.risk_evaluate_method, entity.getEvaluateMethod()));
        excelVO.setRiskLevel(DictResolver.getItemLabelByValue(RiskSysDictEnum.risk_level, entity.getRiskLevel()));
        excelVO.setIdentifyMethod(DictResolver.getItemLabelByValue(RiskSysDictEnum.risk_identify_method, entity.getIdentifyMethod()));
        excelVO.setIdentifyUserId(DictResolver.getItemLabelByValue(AdminDictEnum.sys_user, entity.getIdentifyUserId()));
        excelVO.setEvaluateDate(DateUtils.limitDay(entity.getEvaluateDate()));
        excelVO.setEvaluateUserId(DictResolver.getItemLabelByValue(AdminDictEnum.sys_user, entity.getEvaluateUserId()));
        excelVO.setReevaluateDate(DateUtils.limitDay(entity.getReevaluateDate()));
        excelVO.setAuditUserId(DictResolver.getItemLabelByValue(AdminDictEnum.sys_user, entity.getAuditUserId()));
        excelVO.setAuditDate(DateUtils.limitDay(entity.getAuditDate()));
        excelVO.setEvaluateMember(DictResolver.getItemLabelByValue(AdminDictEnum.sys_user, entity.getEvaluateMember()));

        return excelVO;
    }

    @Override
    public Page<RiskUnitVO> listPage(Page<RiskUnitEntity> page, RiskUnitEntity riskUnit) {
        /* 分页查询 */
        riskUnit.setDelFlag(CommonConstants.STATUS_NORMAL);
        // 禁止查询总数
//        page.setSearchCount(false);
        final Page<RiskUnitEntity> selectPage = this.getBaseMapper().listPage(page, riskUnit);
        final Page<RiskUnitVO> riskUnitPageInfo = new Page<>(page.getCurrent(), page.getSize(), selectPage.getTotal());
        if (CollectionUtils.isEmpty(selectPage.getRecords())) {
            return riskUnitPageInfo;
        }

        /* 查询 双体系排查任务 */
        final Map<Long, Long> taskCountMap = riskMeasureTaskMapper.selectList(Wrappers.lambdaQuery())
                .parallelStream().filter(item -> Objects.nonNull(item.getRiskUnitId()))
                .collect(Collectors.groupingBy(RiskMeasureTaskEntity::getRiskUnitId, Collectors.counting()));
        final List<RiskUnitVO> collect = selectPage.getRecords().parallelStream().map(item -> {
            final RiskUnitVO riskUnitVO = new RiskUnitVO();
            BeanUtils.copyProperties(item, riskUnitVO);
            riskUnitVO.setTaskCount(MapUtil.getLong(taskCountMap, item.getId(), 0L));
            return riskUnitVO;
        }).collect(Collectors.toList());
        riskUnitPageInfo.setRecords(collect);

        final Map<Long, List<RiskUnitVO>> map = riskUnitPageInfo.getRecords().stream().collect(Collectors.groupingBy(RiskUnitVO::getRiskObjectId));
        map.forEach((k,v) -> v.get(0).setRowCount(v.size()));

        /* 查询风险单元的现状风险等级， 取最高等级 */
        final List<RiskUnitEventEntity> riskUnitEventList = riskUnitEventMapper.selectList(Wrappers.<RiskUnitEventEntity>lambdaQuery());
        if (CollectionUtils.isEmpty(riskUnitEventList)) {
            return riskUnitPageInfo;
        }

        /* 根据预设的权重获取最大的风险等级 */
        final Map<Long, Integer> riskUnitEventMaxLevelMap = buildMaxRiskLevelGroupRiskUnitId(riskUnitEventList, RiskUnitEventEntity::getRiskUnitId, RiskUnitEventEntity::getRiskLevel);
        final Map<Long, Integer> riskUnitEventMaxAfterLevelMap = buildMaxRiskLevelGroupRiskUnitId(riskUnitEventList, RiskUnitEventEntity::getRiskUnitId, RiskUnitEventEntity::getAfterRiskLevel);

        riskUnitPageInfo.getRecords().forEach(item -> {
            if (null == item.getRiskLevel() && riskUnitEventMaxLevelMap.containsKey(item.getId())) {
                item.setRiskLevel(MapUtil.getInt(riskUnitEventMaxLevelMap, item.getId(), 0));
            }
            if (riskUnitEventMaxAfterLevelMap.containsKey(item.getId())) {
                item.setAfterRiskLevel(MapUtil.getInt(riskUnitEventMaxAfterLevelMap, item.getId(), 0));
            }
        });

        return riskUnitPageInfo;
    }

    /**
     * 根据预设的权重，获取最高的风险等级
     * @param riskUnitEventList 风险事件
     * @param groupMapper       分组映射
     * @param mapper            方法映射
     * @return                  结果集
     */
    private Map<Long, Integer> buildMaxRiskLevelGroupRiskUnitId(List<RiskUnitEventEntity> riskUnitEventList,
                                                                Function<RiskUnitEventEntity, Long> groupMapper, Function<RiskUnitEventEntity, Integer> mapper) {
        final Map<Long, Integer> map = new HashMap<>();
        final Map<Long, List<RiskUnitEventEntity>> collect = riskUnitEventList.stream().filter(e -> null != groupMapper.apply(e)).collect(Collectors.groupingBy(groupMapper));
        for (Map.Entry<Long, List<RiskUnitEventEntity>> entry : collect.entrySet()) {
            // 风险等级 数值越小、等级越大
            final Optional<Integer> min = entry.getValue().stream().map(mapper).filter(Objects::nonNull).min(Comparator.comparingInt(Integer::intValue));
            map.put(entry.getKey(), min.orElse(BigDecimal.ZERO.intValue()));
        }
        return map;
    }

    @Override
    public Integer getCountLByRiskObjectId(Long id) {
        return this.baseMapper.getCountLByRiskObjectId(id);
    }

    @Override
    public R removeBatchByIds(Long[] ids) {
        for (Long id : ids) {
            RiskUnitEventEntity one = riskUnitEventService.getOne(Wrappers.<RiskUnitEventEntity>lambdaQuery().eq(RiskUnitEventEntity::getRiskUnitId, id), false);
            if (one != null) {
                return R.failed("请先删除绑定风险事件!，例：" + one.getEventName());
            }
        }

        return R.ok(super.removeBatchByIds(CollUtil.toList(ids)));
    }
}
